#ifndef __DICT_H__
#define __DICT_H__

/*
===============================================================================

Key/value dictionary

This is a dictionary class that tracks an arbitrary number of key / value
Pair combinations. It is used for map entity spawning, GUI state management,
and other things.

Keys are compared case-insensitive.

Does not allocate memory until the first key/value Pair is added.

===============================================================================
*/
namespace inf {
    class KeyValue {
        friend class Dict;

    public:
        const Str &		get_key( void ) const { return *key; }
        const Str &		get_value( void ) const { return *value; }

        size_t			allocated( void ) const { return key->allocated() + value->allocated(); }
        size_t			total_size( void ) const { return sizeof( *this ) + key->total_size() + value->total_size(); }

        bool			operator==( const KeyValue &kv ) const { return ( key == kv.key && value == kv.value ); }

    private:
        const PoolStr *	key;
        const PoolStr *	value;
    };

    class Dict {
    public:
        Dict( void );
        Dict( const Dict &other );	// allow declaration with assignment
        ~Dict( void );

        // set the granularity for the index
        void				set_granularity( int granularity );
        // set hash size
        void				set_hash_size( int hashSize );
        // clear existing key/value pairs and copy all key/value pairs from other
        Dict&				operator=( const Dict &other );
        // copy from other while leaving existing key/value pairs in place
        void				copy( const Dict &other );
        // clear existing key/value pairs and transfer key/value pairs from other
        void				transfer_keyvalues( Dict &other );
        // parse dict from lexer
        //bool				Parse( Lexer &parser );

        // copy key/value pairs from other dict not present in this dict
        void				set_defaults( const Dict *dict );
        // clear dict freeing up memory
        void				clear( void );
        // print the dict
        void				print() const;

        size_t				allocated( void ) const;
        size_t				total_size( void ) const { return sizeof( *this ) + allocated(); }

        bool				set( const char *key, const char *value );
        bool				set_float( const char *key, float val );
        bool				set_int( const char *key, int val );
        bool				set_bool( const char *key, bool val );
        bool				set_Vec3( const char *key, const Vec3 &val );
        bool				set_Vec2( const char *key, const Vec2 &val );
        bool				set_Vec4( const char *key, const Vec4 &val );
        bool				set_Angles( const char *key, const Angles &val );
        bool				set_Mat3( const char *key, const Mat3 &val );

        bool				set( int index, const char *value );
        bool				set_float( int index, float val );
        bool				set_int( int index, int val );
        bool				set_bool( int index, bool val );
        bool				set_Vec3( int index, const Vec3 &val );
        bool				set_Vec2( int index, const Vec2 &val );
        bool				set_Vec4( int index, const Vec4 &val );
        bool				set_Angles( int index, const Angles &val );
        bool				set_Mat3( int index, const Mat3 &val );

        // these return default values of 0.0, 0 and false
        const char *		get_string( const char *key, const char *defaultString = "" ) const;
        float				get_float( const char *key, const char *defaultString = "0" ) const;
        int					get_int( const char *key, const char *defaultString = "0" ) const;
        bool				get_bool( const char *key, const char *defaultString = "0" ) const;
        Vec3				get_Vec3( const char *key, const char *defaultString = NULL ) const;
        Vec2				get_Vec2( const char *key, const char *defaultString = NULL ) const;
        Vec4				get_Vec4( const char *key, const char *defaultString = NULL ) const;
        Angles				get_Angles( const char *key, const char *defaultString = NULL ) const;
        Mat3				get_Mat3( const char *key, const char *defaultString = NULL ) const;
        Color3				get_Color3( const char *key, const char *defaultString = NULL ) const;
        Color4				get_Color4( const char *key, const char *defaultString = NULL ) const;

        bool				get_string( const char *key, const char *defaultString, const char **out ) const;
        bool				get_string( const char *key, const char *defaultString, Str &out ) const;
        bool				get_float( const char *key, const char *defaultString, float &out ) const;
        bool				get_int( const char *key, const char *defaultString, int &out ) const;
        bool				get_bool( const char *key, const char *defaultString, bool &out ) const;
        bool				get_Vec3( const char *key, const char *defaultString, Vec3 &out ) const;
        bool				get_Vec2( const char *key, const char *defaultString, Vec2 &out ) const;
        bool				get_Vec4( const char *key, const char *defaultString, Vec4 &out ) const;
        bool				get_Angles( const char *key, const char *defaultString, Angles &out ) const;
        bool				get_Mat3( const char *key, const char *defaultString, Mat3 &out ) const;
        bool                get_Color3( const char *key, const char *defaultString, Color3 &out ) const;
        bool                get_Color4( const char *key, const char *defaultString, Color4 &out ) const;

        int					get_num_keyvalues( void ) const;
        const KeyValue *	get_keyvalues( int index ) const;
        // returns the key/value Pair with the given key
        // returns NULL if the key/value Pair does not exist
        const KeyValue *	find_key( const char *key ) const;
        // returns the index to the key/value Pair with the given key
        // returns -1 if the key/value Pair does not exist
        int					find_key_index( const char *key ) const;
        // returns the index to the k/v Pair with the given key, creates an empty one if not found
        int					generate_key( const char *key );
        // delete the key/value Pair with the given key
        void				delete_key( const char *key );
        // finds the next key/value Pair with the given key prefix.
        // lastMatch can be used to do additional searches past the first match.
        const KeyValue *	match_prefix( const char *prefix, const KeyValue *lastMatch = NULL ) const;
        // randomly chooses one of the key/value pairs with the given key prefix and returns it's value
        const char *		random_prefix( const char *prefix, Random &random ) const;

        // returns a unique checksum for this dictionary's content
        //unsigned int		Checksum( void ) const;

        void				swap( Dict& rhs );

        //static void			SetGlobalPools( StrPool* globalKeys, StrPool* globalValues );
        //static void			GetGlobalPools( StrPool*& _globalKeys, StrPool*& _globalValues );

        static StrPool*		get_global_keys();
        static StrPool*		get_global_values();

        //static void			init( void );
        //static void			shutdown( void );

        //static void			ShowMemoryUsage_f( const idCmdArgs &args );

    private:
        List<KeyValue>	    args;
        HashIndex			argHash;

        //static StrPool*	globalKeys;
        //static StrPool*	globalValues;
    };


    INF_INLINE Dict::Dict( void ) {
        get_global_keys();
        get_global_values();

        args.set_granularity( 16 );
        argHash.set_granularity( 16 );
        argHash.clear( 128, 16 );
    }

    INF_INLINE Dict::Dict( const Dict &other ) {
        get_global_keys();
        get_global_values();

        *this = other;
    }

    INF_INLINE Dict::~Dict( void ) {
        clear();
    }

    INF_INLINE void Dict::set_granularity( int granularity ) {
        args.set_granularity( granularity );
        argHash.set_granularity( granularity );
    }

    INF_INLINE void Dict::set_hash_size( int hashSize ) {
        if ( args.size() == 0 ) {
            argHash.clear( hashSize, 16 );
        }
    }

    INF_INLINE bool Dict::set_float( int index, float val ) {
        return set( index, va( "%f", val ) );
    }

    INF_INLINE bool Dict::set_int( int index, int val ) {
        return set( index, va( "%i", val ) );
    }

    INF_INLINE bool Dict::set_bool( int index, bool val ) {
        return set( index, val ? "1" : "0" );
    }

    INF_INLINE bool Dict::set_Vec3( int index, const Vec3 &val ) {
        return set( index, val.to_string() );
    }

    INF_INLINE bool Dict::set_Vec4( int index, const Vec4 &val ) {
        return set( index, val.to_string() );
    }

    INF_INLINE bool Dict::set_Vec2( int index, const Vec2 &val ) {
        return set( index, val.to_string() );
    }

    INF_INLINE bool Dict::set_Angles( int index, const Angles &val ) {
        return set( index, val.to_string() );
    }

    INF_INLINE bool Dict::set_Mat3( int index, const Mat3 &val ) {
        return set( index, val.to_string( 6 ) );
    }

    INF_INLINE bool Dict::set_float( const char *key, float val ) {
        return set( key, va( "%f", val ) );
    }

    INF_INLINE bool Dict::set_int( const char *key, int val ) {
        return set( key, va( "%i", val ) );
    }

    INF_INLINE bool Dict::set_bool( const char *key, bool val ) {
        return set( key, val ? "1" : "0" );
    }

    INF_INLINE bool Dict::set_Vec3( const char *key, const Vec3 &val ) {
        return set( key, val.to_string() );
    }

    INF_INLINE bool Dict::set_Vec4( const char *key, const Vec4 &val ) {
        return set( key, val.to_string() );
    }

    INF_INLINE bool Dict::set_Vec2( const char *key, const Vec2 &val ) {
        return set( key, val.to_string() );
    }

    INF_INLINE bool Dict::set_Angles( const char *key, const Angles &val ) {
        return set( key, val.to_string() );
    }

    INF_INLINE bool Dict::set_Mat3( const char *key, const Mat3 &val ) {
        return set( key, val.to_string( 6 ) );
    }

    INF_INLINE bool Dict::get_string( const char *key, const char *defaultString, const char **out ) const {
        const KeyValue *kv = find_key( key );
        if ( kv ) {
            *out = kv->get_value();
            return true;
        }
        *out = defaultString;
        return false;
    }

    INF_INLINE bool Dict::get_string( const char *key, const char *defaultString, Str &out ) const {
        const KeyValue *kv = find_key( key );
        if ( kv ) {
            out = kv->get_value();
            return true;
        }
        out = defaultString;
        return false;
    }

    INF_INLINE const char *Dict::get_string( const char *key, const char *defaultString ) const {
        const KeyValue *kv = find_key( key );
        if ( kv ) {
            return kv->get_value();
        }
        return defaultString;
    }

    INF_INLINE float Dict::get_float( const char *key, const char *defaultString ) const {
        return static_cast< float >( Str::to_float( get_string( key, defaultString ) ) );
    }

    INF_INLINE int Dict::get_int( const char *key, const char *defaultString ) const {
        return Str::to_integer( get_string( key, defaultString ) );
    }

    INF_INLINE bool Dict::get_bool( const char *key, const char *defaultString ) const {
        return ( Str::to_integer( get_string( key, defaultString ) ) != 0 );
    }

    INF_INLINE Vec3 Dict::get_Vec3( const char *key, const char *defaultString ) const {
        Vec3 out;
        get_Vec3( key, defaultString, out );
        return out;
    }

    INF_INLINE Vec2 Dict::get_Vec2( const char *key, const char *defaultString ) const {
        Vec2 out;
        get_Vec2( key, defaultString, out );
        return out;
    }

    INF_INLINE Vec4 Dict::get_Vec4( const char *key, const char *defaultString ) const {
        Vec4 out;
        get_Vec4( key, defaultString, out );
        return out;
    }

    INF_INLINE Angles Dict::get_Angles( const char *key, const char *defaultString ) const {
        Angles out;
        get_Angles( key, defaultString, out );
        return out;
    }

    INF_INLINE Mat3 Dict::get_Mat3( const char *key, const char *defaultString ) const {
        Mat3 out;
        get_Mat3( key, defaultString, out );
        return out;
    }

    INF_INLINE Color3 Dict::get_Color3( const char *key, const char *defaultString ) const {
        Color3 out;
        get_Color3( key, defaultString, out );
        return out;
    }

    INF_INLINE Color4 Dict::get_Color4( const char *key, const char *defaultString ) const {
        Color4 out;
        get_Color4( key, defaultString, out );
        return out;
    }

    INF_INLINE int Dict::get_num_keyvalues( void ) const {
        return args.size();
    }

    INF_INLINE const KeyValue *Dict::get_keyvalues( int index ) const {
        if ( index >= 0 && index < args.size() ) {
            return &args[ index ];
        }
        return NULL;
    }

    INF_INLINE void Dict::swap( Dict& rhs ) {
        args.swap( rhs.args );
        argHash.swap( rhs.argHash );
    }

}   // ::inf

#endif /* !__DICT_H__ */
